package com.mumu.muPictureBackend.manager.upload;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import com.mumu.muPictureBackend.config.CosClientConfig;
import com.mumu.muPictureBackend.exception.BusinessException;
import com.mumu.muPictureBackend.exception.ErrorCode;
import com.mumu.muPictureBackend.manager.CosManager;
import com.mumu.muPictureBackend.model.vo.UploadPictureResult;
import com.qcloud.cos.model.PutObjectResult;
import com.qcloud.cos.model.ciModel.persistence.CIObject;
import com.qcloud.cos.model.ciModel.persistence.ImageInfo;
import com.qcloud.cos.model.ciModel.persistence.ProcessResults;
import io.reactivex.Completable;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * 图片上传模板 定义公共的流程 这里公共模板的意义: 1. 上传方式可以是file本地图片上传 还有一个就是url上传 2. 它们的上传方式流程都是一致的
 * 只是一个校验URL地址 一个需要校验file文件 还有就是下载方式不一样
 * 所以就用一个抽象模板类 将有差异的代码块给抽象出来 让它们来继承这个抽象模板类 这个抽象模板类中放的就是它们的共有代码 然后它们的独有不同的代码就由它们类中自己去实现即可
 */
@Slf4j
public abstract class PictureUploadTemplate {
    //cos阿里云的配置类(里面有key,桶名称等配置)
    @Resource
    private CosClientConfig cosClientConfig;
    //文件上传类
    @Resource
    private CosManager cosManager;

    /**
     *
     * @param inputSource      图片包装类
     * @param uploadPathPrefix 上传的图片后缀名字
     * @return 返回该路径的基本信息 包括名字 大小 宽度 高度 宽高比 封装起来之后返回
     */
    public UploadPictureResult uploadRequest(Object inputSource, String uploadPathPrefix) {

        //1. 校验文件(文件大小,后缀名是否合格)(公用模板)
        ValidPicture(inputSource);
        //2. 指定路径 文件名字就是 原始文件名+uuid+日期 即可 uuid是防止重复
        String uuid = RandomUtil.randomNumbers(16);
        //提取文件的名字(公用模板)
        String originalFilename = getOriginalFilename(inputSource);
        //拼接完成的路径
        String filePath = String.format("%s_%s.%s", DateUtil.formatDate(new Date()), uuid, originalFilename);
        String uploadPath = String.format("%s/%s", uploadPathPrefix, filePath);//根据文件名前缀来分类文件夹 统一管理 这个前缀可以是public:/userid 这样就根据每个用户来分类文件了
        //3. 上传图片到cos中 思路:先将该文件写入到本地磁盘(利用file) 再从本地中读取该文件 然后将该文件上传到COS中 上传后就将该本地文件删除
        //将file定义到try外面 在try里面用 finally中销毁
        File file = null;
        try {
            //文件上传后需要返回文件信息 文件信息都封装到这个里面了
            UploadPictureResult uploadPictureResult = new UploadPictureResult();
            //1. 先将文件存储到本地 null只是不指定后缀名 因为具体后缀path中已经指定
            String localFilePath = "pulic/" + UUID.randomUUID();
            file = File.createTempFile(localFilePath, null);
            //2. 将文件写到本地文件中
            transferToLocalFile(inputSource, file);
            //3. 将图片file上传到cos上 然后将图片信息封装到 uploadPictureResult 中 最后返回
            uploadPictureToCos(uploadPath, file, uploadPictureResult, originalFilename);

            //返回可访问该图片的参数
            return uploadPictureResult;

        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            //将临时文件删除 该文件已经用完了 那么就删除
            if (file != null) {
                boolean delete = file.delete();
                if (!delete) {
                    log.info("删除失败 文件路径{}", file.getPath());
                }
            } else {
                log.info("无文件upload:{}", uploadPath);
            }
        }
    }


    /**
     * 将文件转移到本地文件file中 (抽象方法必须被子类实现 所以必须是public而不是private)
     *
     * @param inputSource
     * @param file
     */
    public abstract void transferToLocalFile(Object inputSource, File file) throws IOException;

    /**
     * 提取原始的文件名
     *
     * @param inputSource
     * @return
     */
    public abstract String getOriginalFilename(Object inputSource);

    /**
     * 校验文件
     *
     * @param multipartFile
     */
    public abstract void ValidPicture(Object multipartFile);


    /**
     * 将文件上传到cos上 并且将该图片信息封装一下
     *
     * @param uploadPath
     * @param file
     * @param uploadPictureResult
     * @param originalFilename
     */
    private void uploadPictureToCos(String uploadPath, File file, UploadPictureResult uploadPictureResult, String originalFilename) {
        //1. 将该文件上传到cos上 返回值是文件的信息封装类   实参:第一个参数是该文件的路径 第二个参数就是上传的文件内容
        PutObjectResult putObjectResult = cosManager.putPictureObject(uploadPath, file);

        //2. 提取图片的信息 然后封装
        ImageInfo imageInfo = putObjectResult.getCiUploadResult().getOriginalInfo().getImageInfo();
        //3. 获取图片压缩处理结果
        ProcessResults processResults = putObjectResult.getCiUploadResult().getProcessResults();
        // 处理转换后的所有的图片
        List<CIObject> objectList = processResults.getObjectList();
        // 5. 压缩成功->上传webp格式信息 如果处理压缩后的图片不为空 说明压缩成功 那么就返回压缩图的信息(webp格式的) 否则就是压缩不成功 那么就返回原图的信息
        if (CollUtil.isNotEmpty(objectList)) {
            //获取压缩后的图片 这个集合的第一个元素是webp格式后的图片 第二个元素就是该图片的缩略图
            CIObject compressedCiObject = objectList.get(0);
            //20KB的图片才生成缩略图 所以这里需要判断一下 可能没有缩略图 没有缩略图默认就是原图了 缩略图的url一定要设置 要不然前端展示啥呢
            CIObject thumbnailCiObject = compressedCiObject;
            if (objectList.size() > 1) {
                //获取缩略图
                thumbnailCiObject = objectList.get(1);
            }
            uploadPictureResult.setThumbnailUrl(cosClientConfig.getHost() + "/" + thumbnailCiObject.getKey());
            buildPictureResult(compressedCiObject, uploadPictureResult);
            return;
        }

        if (imageInfo == null) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "无法获取图片信息");
        }
        //5. 压缩不成功->上传原图信息 将图片的信息都封装到uploadPictureResult中然后返回 压缩不成功 返回原图信息
        buildPictureResult(uploadPath, file, uploadPictureResult, originalFilename, imageInfo);
    }

    /**
     * 将图片们的信息封装到uploadPictureResult中
     *
     * @param imageInfo
     * @param uploadPictureResult
     */
    private void buildPictureResult(CIObject imageInfo, UploadPictureResult uploadPictureResult) {
        int width = imageInfo.getWidth();
        int height = imageInfo.getHeight();
        double picScale = width * 1.0 / height;//宽高比;
        uploadPictureResult.setUrl(cosClientConfig.getHost() + "/" + imageInfo.getKey());
        uploadPictureResult.setName(FileUtil.mainName(imageInfo.getKey()));//提取文件名 不包含后缀
        uploadPictureResult.setPicSize(imageInfo.getSize().longValue());
        uploadPictureResult.setPicWidth(width);
        uploadPictureResult.setPicHeight(height);
        uploadPictureResult.setPicScale(picScale);

        uploadPictureResult.setPicFormat(imageInfo.getFormat());
    }

    private void buildPictureResult(String uploadPath, File file, UploadPictureResult uploadPictureResult, String originalFilename, ImageInfo imageInfo) {
        int width = imageInfo.getWidth();
        int height = imageInfo.getHeight();
        double picScale = width * 1.0 / height;//宽高比;
        uploadPictureResult.setUrl(cosClientConfig.getHost() + "/" + uploadPath);
        uploadPictureResult.setName(FileUtil.mainName(originalFilename));//提取文件名 不包含后缀
        uploadPictureResult.setPicSize(FileUtil.size(file));
        uploadPictureResult.setPicWidth(width);
        uploadPictureResult.setPicHeight(height);
        uploadPictureResult.setPicScale(picScale);
        uploadPictureResult.setPicFormat(imageInfo.getFormat());
    }


}
